//
//  smcJaikuAPI.m
//  juhu
//
//  Created by Christian Martick on 26.07.07.
//  Copyright 2007 Christian Martick. All rights reserved.
//
//	jaiku api - recent posts of all jaiku users - public timeline sort of
//
//	this objects handle the users contacts posts using rss feed
//
//	Copyright (C) 2007 Christian Martick, http://juhu-mac.blogspot.com
//
//	Permission is hereby granted, free of charge, to any person obtaining a copy
//	of this software and associated documentation files (the "Software"), to deal
//	in the Software without restriction, including without limitation the rights
//	to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
//	copies of the Software, and to permit persons to whom the Software is
//	furnished to do so, subject to the following conditions:
//
//	The above copyright notice and this permission notice shall be included in
//	all copies or substantial portions of the Software.
//
//	THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
//	IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
//	FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
//	AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
//	LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
//	OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
//	THE SOFTWARE.

#import "smcJaikuAPI.h"

@implementation smcJaikuAPI

// init with parameters
- (id) initWithUsername: (NSString *) username andPersonalKey: (NSString *) personalKey
{
	//init with key and username
	self = [super init];
	if (self != nil) {
		_accessToken = nil;
		_accessSecret = nil;
		_currentOAuthToken = nil;
		_currentOAuthTokenSecret = nil;
		
		_personalKey = [[NSString alloc] initWithString: personalKey];
		_username = [[NSString alloc] initWithString: username];
		_doingUpdate = NO;
		_doingContactUpdate = NO;
		_contactUpdateError = NO;
		_imagesStillToCatch = 0;
		_flickrStillToCatch = 0;
		_indexesNew = nil;
		_indexesOld = nil;
		_delegate = nil;
		_contacts = nil;
		_streams = [[NSMutableDictionary dictionaryWithObjectsAndKeys:
			nil, @"public",
			nil, @"private"] retain];
		_userImages = [[NSMutableDictionary alloc] initWithCapacity: 20];
	}
	return self;
}

// dealloc all objects
- (void) dealloc 
{
	[_indexesNew release];
	[_indexesOld release];
	[_contacts release];
	[_streams release];
	[_username release];
	[_personalKey release];
	[super dealloc];
}

// general setters and getters

- (NSImage *) getImageForUser: (NSString *) nick
{
	if ([_userImages objectForKey: nick] == nil)
		return [NSImage imageNamed: @"avatar"];
	
	return [_userImages objectForKey: nick];
}

// only allow changes when not doing an update
- (BOOL) setUsername: (NSString *) username
{
	if (_doingUpdate)
		return NO;

	[_username release];
	_username = [[NSString alloc] initWithString: username];
	return YES;
}

- (NSString *) username
{
	return _username;
}

- (BOOL) setPersonalKey: (NSString *) personalKey
{
	if (_doingUpdate)
		return NO;

	[_personalKey release];
	_personalKey = [[NSString alloc] initWithString: personalKey];
	return YES;
}

- (NSString *) personalKey
{
	return _personalKey;
}

- (BOOL) setDelegate: (id) delegate
{
	// only set if delegate confirms to the smcJaikuAPIDelegate protocol
	if (![delegate conformsToProtocol: @protocol(smcJaikuAPIDelegate)] || _doingUpdate)
		return NO;
	_delegate = delegate;
	return YES;	
}

- (id) delegate
{
	return _delegate;
}

// testing credentials
- (NSError *) testAccountCredentials
{
	//do empty post to verify credentials
	//returns nil if credentials are good

	//check if _username and password are neq nil
	if ((_username == nil) || (_personalKey == nil))
		return [self missingUsernameOrPasswordErrorWithDomain: @"jaikuTestAccount"];
	
	//posting empty message and hoping for the best
	NSError *result = [self postMessage: @"" withLocation: @"" andIcon: kSmcIconNone generated: YES];

	//something went wrong, only report error if server found but no post possible -> credentials are most likely wrong
	if ((result != nil) && ([result code] == kSmcErrorServerFoundButNoPost))
		return [NSError errorWithDomain: @"jaikuTestAccount"
			code: kSmcErrorCredentialsWrong
			userInfo: [NSDictionary dictionaryWithObjectsAndKeys: 
				@"Credentials might be wrong", @"errorAbstract",
				@"It was not possible to to log in with the given username and personal key, they might be wrong", @"errorText", NULL]];
	else
		return nil;
}

// help methods
- (NSError *) missingUsernameOrPasswordErrorWithDomain: (NSString *) domain
{
	// one error that could reoccure...
	return [NSError errorWithDomain: domain
				code: kSmcErrorMissingNameOrPassword
				userInfo: [NSDictionary dictionaryWithObjectsAndKeys: 
					@"Could not post message", @"errorAbstract",
					@"The computer seems to be offline, therefore the message could not be posted.", @"errorText", NULL]];
}

- (NSString *) urlEncodeValue: (NSString *) string
{
	//mask & chars...
	NSString *result = (NSString *) CFURLCreateStringByAddingPercentEscapes(kCFAllocatorDefault, (CFStringRef)string, NULL, CFSTR("?=&+"), kCFStringEncodingUTF8);
	return [result autorelease];
}

- (NSMutableString *) makeCleanUTF8MutableStringFromData: (NSData *) theData;
{
	//okay, sometimes data isn't clean utf-8 string data!
	
	//check first
	NSMutableString *ret = [[[NSMutableString alloc] initWithData: theData encoding: NSUTF8StringEncoding] autorelease];
	if (ret != nil)
		return ret;
	
	//so it isn't, make it utf8
	//char by char remove all that are non-utf-8
	//1byte long, 2byte long ... no style here, plain hack...
	
	NSMutableData *mutableData = [NSMutableData dataWithData: theData];
	NSString *placeholder = [NSString stringWithFormat:@"%C ",0xfffd]; // 4byte replacement (=3byte+space)
	NSString *sub = nil;
				
	for (int i = 0; i < [theData length]; i++)
	{
		sub = [[[NSString alloc] initWithData: [theData subdataWithRange: NSMakeRange( i, 1)] encoding: NSUTF8StringEncoding] autorelease];
		if (sub == nil)
		{
			//nope, last char isn't ascii
			sub = [[[NSString alloc] initWithData: [theData subdataWithRange: NSMakeRange( i, 2)] encoding: NSUTF8StringEncoding] autorelease];
			if (sub == nil)
			{
				//isn't 2 byte 
				sub = [[[NSString alloc] initWithData: [theData subdataWithRange: NSMakeRange( i, 3)] encoding: NSUTF8StringEncoding] autorelease];
				if (sub == nil)
				{
					//isnt' 3 byte
					sub = [[[NSString alloc] initWithData: [theData subdataWithRange: NSMakeRange( i, 4)] encoding: NSUTF8StringEncoding] autorelease];
					if (sub == nil)
					{
						//isn't utf-8 at all
						[mutableData replaceBytesInRange: NSMakeRange( i,4) withBytes: [placeholder UTF8String]];
						
					}
					i+= 3;
				}
				else
					i+=2;
			}
			else
				i+=1;
		}
	}
	
	//now it should be utf-8
	return [[[NSMutableString alloc] initWithData: mutableData encoding: NSUTF8StringEncoding] autorelease];
}
	

@end
